/**
 * @Author 天下无敌
 * @Date 
 * @AIDE AIDE+ 
 */

package com.ecs.core.system;
import com.artemis.annotations.All;
import com.artemis.systems.IteratingSystem;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.ReflectionException;
import com.ecs.core.components.StateCom;
import com.ecs.core.other.state.IEntityState;

@All({StateCom.class})
public class StateSystem extends IteratingSystem
{

	private ComponentMapper<StateCom> states;
	
	@Override
	protected void process(int p)
	{
		StateCom state = states.get(p);
		
		if(state.currentState != null){
			state.currentState.update(world.getEntity(p));
		}
	}

	//状态切换相关
	public static final ObjectMap<Class<?>, Pool<?>> statePool = new ObjectMap<>();

	public static <C extends IEntityState> void changeState(Entity e, Class<C> type) {
		StateCom state = e.getComponent(StateCom.class);
		if (state == null) throw new IllegalArgumentException("Entity missing StateCom component");
		
		IEntityState oldState = state.currentState;
		if (oldState != null) {
			oldState.exit(e);

			IEntityState oldPrevious = state.previousState;
			state.previousState = oldState;

			if (oldPrevious != null) {
				free(oldPrevious);
			}
		}

		C newState = getState(type);
		state.currentState = newState;
		newState.enter(e);
	}

	@SuppressWarnings("unchecked")
	private static <C extends IEntityState> void free(C state) {
		Pool<C> pool = (Pool<C>) statePool.get(state.getClass());

		if (pool == null) {
			pool = new Pool<C>() {
				@Override
				protected C newObject() {
					try {
						return (C)ClassReflection.newInstance(state.getClass());
					} catch (ReflectionException e) {
						throw new RuntimeException("Failed to create state", e);
					}
				}
			};
			statePool.put(state.getClass(), pool);
		}
		pool.free(state);
	}

	@SuppressWarnings("unchecked")
	private static <C extends IEntityState> C getState(Class<C> type) {
		Pool<C> pool = (Pool<C>) statePool.get(type);

		if (pool == null) {
			pool = new Pool<C>() {
				@Override
				protected C newObject() {
					try {
						return ClassReflection.newInstance(type);
					} catch (ReflectionException e) {
						throw new RuntimeException("Failed to create state", e);
					}
				}
			};
			statePool.put(type, pool);
		}
		return pool.obtain();
	}
}
